package com.zwps.biz.domain.service.impl;

import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zwps.biz.api.enums.FileStoreCatalogueEnum;
import com.zwps.biz.dal.db.dao.FileRecordDAO;
import com.zwps.biz.dal.db.dao.FileTypeDAO;
import com.zwps.biz.dal.db.object.FileRecordDO;
import com.zwps.biz.dal.db.object.FileTypeDO;
import com.zwps.biz.domain.converter.FileRecordConverter;
import com.zwps.biz.domain.enums.FileStoreTypeEnum;
import com.zwps.biz.domain.enums.FileUploadStatusEnum;
import com.zwps.biz.domain.enums.FileUseTypeEnum;
import com.zwps.biz.domain.model.pojo.FileUploadResultPOJO;
import com.zwps.biz.domain.service.FileManageService;
import com.zwps.biz.domain.service.FileRecordBizService;
import com.zwps.biz.domain.service.FileRecordService;
import com.zwps.biz.domain.service.FileStoreService;
import com.zwps.biz.api.model.dto.file.UploadFileByUrlDTO;
import com.zwps.biz.api.model.dto.file.record.AddFileRecordDTO;
import com.zwps.biz.api.model.dto.file.record.biz.AddFileRecordBizDTO;
import com.zwps.biz.api.model.vo.file.FileUploadInfoVO;
import com.zwps.common.core.exception.UserActionException;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StreamUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Slf4j
@Service
@AllArgsConstructor
public class FileManageServiceImpl implements FileManageService {

    private FileStoreService fileStoreService;

    private FileRecordService fileRecordService;

    private FileRecordDAO fileRecordDAO;

    private FileTypeDAO fileTypeDAO;

    private FileRecordBizService fileRecordBizService;

    // 当找不到对应的文件类时，使用默认的文件类（文件）
    private final Integer DEFAULT_FILE_TYPE = 0;

    @Override
    public FileUploadInfoVO uploda(MultipartFile file, String path) {
        String fileName = file.getOriginalFilename();
        if ("".equals(fileName)) {
            fileName = file.getName();
        }
        try {
            InputStream stream = new ByteArrayInputStream(StreamUtils.copyToByteArray(file.getInputStream()));
            String fileType = FileTypeUtil.getType(stream, fileName);
            stream.reset();
            return this.uploda(stream, path, fileName, fileType, file.getSize(), FileUseTypeEnum.ORIGINAL, null);
        } catch (Exception e) {
            if (e instanceof UserActionException) {
                throw (UserActionException) e;
            }
            log.error("upload file[{}] error: {}", fileName, e);
            throw new UserActionException("上传文件失败");
        }
    }

    @Transactional
    @Override
    public FileUploadInfoVO uploda(InputStream stream, String path, String fileName, String fileType, Long fileSize,
            FileUseTypeEnum useType, String parentId) {
        String fileId = null;
        try {
            if (!(stream instanceof FileInputStream)) {
                stream.reset();
            }
            FileRecordDO record = new FileRecordDO();
            record.setUploadStatus(FileUploadStatusEnum.UPLOADING.getCode());
            record.setFileType(fileType);
            record.setFileName(fileName);
            record.setFileSize(fileSize);
            record.setFileClass(findFileClass(fileType));
            record.setUseType(useType.getCode());
            record.setParentId(parentId);
            record.setExternalFile(0);
            fileRecordService.addFileRecord(record);
            fileId = record.getId();

            String newFileName = record.getId() + fileName.substring(fileName.lastIndexOf("."));
            FileUploadResultPOJO result = fileStoreService.upload(stream, path, newFileName);
            record.setFilePath(result.getFilePath());
            record.setFileUrl(result.getFileUrl());
            LambdaUpdateWrapper<FileRecordDO> update = Wrappers.lambdaUpdate(FileRecordDO.class);
            update.set(FileRecordDO::getFilePath, result.getFilePath());
            update.set(FileRecordDO::getFileUrl, result.getFileUrl());
            update.set(FileRecordDO::getStoreType, result.getStoreType().getCode());
            update.set(FileRecordDO::getUploadStatus, FileUploadStatusEnum.SUCCESS.getCode());
            update.eq(FileRecordDO::getId, record.getId());
            if (fileRecordDAO.update(null, update) < 1) {
                throw new UserActionException("保存文件记录地址失败");
            }

            FileUploadInfoVO info = FileRecordConverter.INSTANCE.toFileUploadInfoVO(record);
            info.setFileUrl(result.getFileUrl());
            info.setFileInternalUrl(result.getFileInternalUrl());
            return info;
        } catch (Exception e) {
            log.error("upload file[{}] error: {}", fileName, e);
            if (fileId != null) {
                LambdaUpdateWrapper<FileRecordDO> update = Wrappers.lambdaUpdate(FileRecordDO.class);
                update.set(FileRecordDO::getUploadStatus, FileUploadStatusEnum.FAIL.getCode());
                update.set(FileRecordDO::getUploadFailReason, e.getMessage());
                update.eq(FileRecordDO::getId, fileId);
                fileRecordDAO.update(null, update);
            }
            throw new UserActionException("上传文件失败");
        }
    }

    private Integer findFileClass(String fileType) {
        LambdaQueryWrapper<FileTypeDO> queryFileType = Wrappers.lambdaQuery(FileTypeDO.class)
                .eq(FileTypeDO::getFileType, fileType);
        FileTypeDO fileTypeDO = fileTypeDAO.selectOne(queryFileType);
        if (fileTypeDO == null) {
            fileTypeDO = new FileTypeDO(fileType);
            fileTypeDAO.insert(fileTypeDO);
            return DEFAULT_FILE_TYPE;
        }
        if (fileTypeDO.getFileClass() == null) {
            return DEFAULT_FILE_TYPE;
        }
        return fileTypeDO.getFileClass();
    }

    @Override
    public List<FileUploadInfoVO> uploda(MultipartFile[] files, String path) {
        ArrayList<FileUploadInfoVO> list = new ArrayList<FileUploadInfoVO>();
        for (MultipartFile multipartFile : files) {
            try {
                list.add(uploda(multipartFile, path));
            } catch (Exception e) {
            }
        }
        return list;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileUploadInfoVO uploadFileByUrl(UploadFileByUrlDTO dto) {
        boolean bindFlag = true;
        if (StrUtil.isEmpty(dto.getTableName())) {
            dto.setTableName(FileStoreCatalogueEnum.TEMP.getName());
            bindFlag =false;
        }
        String fileName = "";
        try {
            URL url = new URL(dto.getUrl());
            // 打开连接
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            // 设置请求方法为GET
            connection.setRequestMethod("GET");
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                // 获取文件名
                String disposition = connection.getHeaderField("content-disposition");
                if (StrUtil.isNotEmpty(dto.getFileName())){
                    fileName = dto.getFileName();
                }else if (disposition != null && !disposition.isEmpty()) {
                    int index = disposition.indexOf("filename=");
                    if (index != -1) {
                        fileName = disposition.substring(index + 9);
                    }
                }
                if (StrUtil.isEmpty(fileName)) {
                    // 如果获取不到文件名，则从URL中截取
                    String path = url.getPath();
                    int slashIndex = path.lastIndexOf('/');
                    if (slashIndex != -1) {
                        fileName = path.substring(slashIndex + 1);
                    }
                }
                // 获取输入流
                InputStream inputStream = connection.getInputStream();
                InputStream stream = new ByteArrayInputStream(StreamUtils.copyToByteArray(inputStream));
                String fileType = FileTypeUtil.getType(stream, fileName);
                stream.reset();
                FileUploadInfoVO fileUploadInfoVO = uploda(stream, dto.getTableName(), fileName, fileType, connection.getContentLength() != -1 ? (long) connection.getContentLength() : 0,
                        FileUseTypeEnum.ORIGINAL, null);
                if (StrUtil.isNotEmpty(dto.getTableId()) && bindFlag) {
                    Set<String> fileId = new HashSet<>();
                    fileId.add(fileUploadInfoVO.getId());
                    AddFileRecordBizDTO fileRecordBizDTO = new AddFileRecordBizDTO(fileId,dto.getTableName(),dto.getTableId());
                    fileRecordBizService.addFileRecordBizs(fileRecordBizDTO);
                }
                return fileUploadInfoVO;
            } else {
                return null;
            }
        } catch (Exception e) {
            if (e instanceof UserActionException) {
                throw (UserActionException) e;
            }
            log.error("upload file[{}] error: {}", fileName, e);
            throw new UserActionException("上传文件失败");
        }
    }

    @Override
    @Transactional
    public List<FileUploadInfoVO> uploadFileByUrlList(List<UploadFileByUrlDTO> dto) {
        List<FileUploadInfoVO> fileUploadInfoList = new ArrayList<>();
        for (UploadFileByUrlDTO file : dto) {
            fileUploadInfoList.add(uploadFileByUrl(file));
        }
        return fileUploadInfoList;
    }

    @Override
    public FileUploadInfoVO addFileRecord(MultipartFile file, String fileName, String fileUrl, Integer externalFile) {
        if ("".equals(fileName)) {
            fileName = file.getName();
        }
        try {

            if (externalFile == 0) {
                //非外部文件
                InputStream stream = new ByteArrayInputStream(StreamUtils.copyToByteArray(file.getInputStream()));
                String fileType = FileTypeUtil.getType(stream, fileName);
                stream.reset();
                return this.uploda(stream, FileStoreCatalogueEnum.TEMP.getCode(), fileName, fileType, file.getSize(), FileUseTypeEnum.ORIGINAL, null);
            }else {
                //外部文件
                InputStream stream = new ByteArrayInputStream(StreamUtils.copyToByteArray(file.getInputStream()));
                String fileType = FileTypeUtil.getType(stream, fileName);
                stream.reset();
                String fileId = null;
                try {
                    stream.reset();
                    FileRecordDO record = new FileRecordDO();
                    record.setFileUrl(fileUrl);
                    record.setStoreType(FileStoreTypeEnum.LOCAL_DISK.getCode());
                    record.setUploadStatus(FileUploadStatusEnum.SUCCESS.getCode());
                    record.setFileType(fileType);
                    record.setFileName(fileName);
                    record.setFileSize(file.getSize());
                    record.setFileClass(findFileClass(fileType));
                    record.setUseType(FileUseTypeEnum.ORIGINAL.getCode());
                    record.setParentId(null);
                    record.setExternalFile(1);
                    fileRecordService.addFileRecord(record);
                    fileId = record.getId();

                    FileUploadInfoVO info = FileRecordConverter.INSTANCE.toFileUploadInfoVO(record);
                    return info;
                } catch (Exception e) {
                    log.error("upload file[{}] error: {}", fileName, e);
                    if (fileId != null) {
                        LambdaUpdateWrapper<FileRecordDO> update = Wrappers.lambdaUpdate(FileRecordDO.class);
                        update.set(FileRecordDO::getUploadStatus, FileUploadStatusEnum.FAIL.getCode());
                        update.set(FileRecordDO::getUploadFailReason, e.getMessage());
                        update.eq(FileRecordDO::getId, fileId);
                        fileRecordDAO.update(null, update);
                    }
                    throw new UserActionException("上传文件失败");
                }
            }

        } catch (Exception e) {
            if (e instanceof UserActionException) {
                throw (UserActionException) e;
            }
            log.error("upload file[{}] error: {}", fileName, e);
            throw new UserActionException("上传文件失败");
        }
    }

    @Override
    public String saveUploadRecord(AddFileRecordDTO dto) {
        FileRecordDO fileRecordDO = FileRecordConverter.INSTANCE.toFileRecordDO(dto);
        if (fileRecordDAO.insert(fileRecordDO)<1){
            throw new UserActionException("保存录制任务文件记录失败！");
        };
        return fileRecordDO.getId();
    }

}
